﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Web;

namespace ASPNETPerformance.App_BusinessLogic.Utils.CodeProfile {
    public static class CodeTimer {
        public static void Initialize() {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
            Time( "", 1, () =>
            {
            } );
        }

        public static string Time( string name, int iteration, Action action ) {
            if ( String.IsNullOrEmpty( name ) )
                return string.Empty;

            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            // 1.
            // ConsoleColor currentForeColor = Console.ForegroundColor;
            // Console.ForegroundColor = ConsoleColor.Yellow;
            // Console.WriteLine( name );
            sb.AppendFormat( "<span style='color:red;'><b>{0}</b></span><Br />", name );

            // 2.
            GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced );
            int[] gcCounts = new int[ GC.MaxGeneration + 1 ];
            for ( int i = 0; i <= GC.MaxGeneration; i++ ) {
                gcCounts[ i ] = GC.CollectionCount( i );
            }

            // 3.
            Stopwatch watch = new Stopwatch();
            watch.Start();
            ulong cycleCount = GetCycleCount();
            for ( int i = 0; i < iteration; i++ )
                action();
            ulong cpuCycles = GetCycleCount() - cycleCount;
            watch.Stop();

            // 4.
            // Console.ForegroundColor = currentForeColor;
            //Console.WriteLine( "\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString( "N0" ) + "ms" );
            // Console.WriteLine( "\tCPU Cycles:\t" + cpuCycles.ToString( "N0" ) );
            sb.AppendFormat( "\tTime Elapsed:\t{0}ms<br />", watch.ElapsedMilliseconds.ToString( "N0" ) );
            sb.AppendFormat( "\tCPU Cycles:\t{0}<br />", cpuCycles.ToString( "N0" ) );

            // 5.
            for ( int i = 0; i <= GC.MaxGeneration; i++ ) {
                int count = GC.CollectionCount( i ) - gcCounts[ i ];
               // Console.WriteLine( "\tGen " + i + ": \t\t" + count );
                sb.AppendFormat( "\tGen {0}: \t\t{1}<br />", i, count );
            }
            // Console.WriteLine();

            sb.AppendFormat( "<br />" );
            return sb.ToString();
        }

        private static ulong GetCycleCount() {
            ulong cycleCount = 0;
            QueryThreadCycleTime( GetCurrentThread(), ref cycleCount );
            return cycleCount;
        }

        [DllImport( "kernel32.dll" )]
        [return: MarshalAs( UnmanagedType.Bool )]
        static extern bool QueryThreadCycleTime( IntPtr threadHandle, ref ulong cycleTime );

        [DllImport( "kernel32.dll" )]
        static extern IntPtr GetCurrentThread();

    }
}